/* 
 * Gneat (http://gneat.org)
 *
 * (C) Copyright 2011 and beyond - Claudio Stella, all rights reserved.
 *
 * This file is part of Gneat.
 *
 * Gneat is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (LGPL)
 * as published by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Gneat is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * and the GNU General Public License, for completeness, along with Gneat.  
 * If not, see <http://www.gnu.org/licenses/>.
 */
package org.neatshell.gneat.client.view.live.core;

import java.util.HashMap;
import java.util.List;

import org.neatshell.gneat.client.GneatCore;
import org.neatshell.gneat.client.event.Events;
import org.neatshell.gneat.client.handler.Handler;
import org.neatshell.gneat.client.handler.Handlers;
import org.neatshell.gneat.client.presenter.core.Presenter;
import org.neatshell.gneat.client.presenter.core.Presenter.IView;
import org.neatshell.gneat.client.presenter.core.manipulation.css.Css;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style.Float;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;

public class LiveView extends Widget implements Presenter.IView {
//	private static final LiveViewImpl viewImpl = GWT.create(LiveViewImpl.class);
	
	Node _node;
	
	@Inject
	protected LiveView() {}
	
	@Override
	public void addEventHandler(String event, Handler handler) {
		Handlers.register(handler, this);
	}
	
	public void append(Presenter.IView view) {
		_node.appendChild((Node) view.getNode());
		((LiveView) view).attach();
	}

	public void append(String html) {
		for (Node node : childNodesFromHtml(html)) _node.appendChild(node);
	}
	
	void attach() {
		if (getNodeType() == 1) {
			if (getElement() != null && !isAttached()) this.onAttach();			
		}
	}

	private Node[] childNodesFromHtml(String html) {
		Element temp = DOM.createDiv();
		temp.setInnerHTML(html);
		NodeList<Node> nodeList = temp.getChildNodes();
		Node[] nodes = new Node[nodeList.getLength()];

		for (int i=0; i<nodeList.getLength(); i++) {
			Node node = nodeList.getItem(i);
//			if (node.getNodeType() == 3) {
//				nodes[i] = Document.get().createTextNode(node.getNodeValue());				
//			}
//			else 
			nodes[i] = node;
		}
		
		//	DOM.removeChild(null, temp);
		temp = null;
		return nodes;
	}

	@Override
	public IView clone() {
		IView view = GneatCore.getCoreInjector().getView().setNode(_node.cloneNode(false));
		return view;
	}

	@Override	
	public void detach() {
		if (!getNodeName().equals("body")) {
			Node parent = _node.getParentNode();
			if (parent != null) parent.removeChild(_node);
		}
	}
		
	@Override
	//TODO check this better
	public String getAttribute(String name) {
		return DOM.getElementAttribute(getElement(), name);
//		return DOM.getElementProperty(getElement(), name);
	}

	@Override
	public String getClassName() {
		return getElement().getClassName();
	}

	@Override
	public String getCss(String name) {
		return DOM.getStyleAttribute(getElement(), name);
	}
	
	private static Document getDocument() {
		return Document.get();
	}
	
	@Override	
	public Object getFirstChildNode() {
		return _node.getFirstChild();
	}
	
	@Override
	public Object getLastChildNode() {
		return _node.getLastChild();
	}
	
	@Override
	public Object getNextNode() {
		return _node.getNextSibling();
	}
	
	@Override
	public Object getNode() {
		return _node;
	}
	
	@Override
	public Object getParentNode() {
		return _node.getParentNode();
	}
	
	@Override
	public Object getPrevNode() {
		return _node.getPreviousSibling();
	}

	@Override
	public String getProperty(String propName) {
		return DOM.getElementProperty(getElement(), propName);
	}
	
	@Override
	public String html() {
		return getElement().getInnerHTML();
	}
		
	@Override
	public void html(String html) {
		getElement().setInnerHTML(html);
	}

	@Override
	public void insertBefore(IView newChild, IView refChild) {
		Node newEl = (Node) newChild.getNode();
		Node refEl = refChild != null ? (Node) refChild.getNode() : null;
		_node.insertBefore(newEl, refEl);
		((LiveView) newChild).attach();
	}

	@Override
	public void insertBefore(String html, IView refChild) {
		Node refEl = refChild != null ? (Node) refChild.getNode() : null;		
		for (Node n : childNodesFromHtml(html)) _node.insertBefore(n, refEl);
	}
	
	@Override
	public String getNodeName() {
		return _node.getNodeName();
	}
	
	@Override
	public short getNodeType() {
		return _node.getNodeType();
	}

	@Override
	public String getNodeValue() {
		return _node.getNodeValue();
	}
	
	@Override
	public void remove() {
		Node parent = _node.getParentNode();
		if (parent != null && !getNodeName().equals("body")) {
			parent.removeChild(_node);
		}		
		_node = null;
//		if (isAttached()) onDetach();
	}

	@Override
	public void removeAttr(String name) {
		DOM.removeElementAttribute(getElement(), name);
	}
	
	@Override
	public void removeEventHandler(List<Handler> handlers) {
		Handlers.unregister(handlers);
	}

	@Override
	public void removeProp(String propName) {
		DOM.setElementProperty(getElement(), propName, "undefined");
//      FIXME which is better?
//		DOM.setElementProperty(getElement(), prop.getName(), "");
	}
	
	@Override
	public void setAttribute(String name, String value) {		
		DOM.setElementAttribute(getElement(), name, value);
//		DOM.setElementProperty(getElement(), name, value);		
	}

	@Override
	public void setClassName(String className) {
		getElement().setClassName(className);
	}

	@Override
	public void setCss(Css css, String value) {
		if (css == Css.FLOAT) {
			if (value.equals("left")) getElement().getStyle().setFloat(Float.LEFT);
			else if (value.equals("right")) getElement().getStyle().setFloat(Float.RIGHT);
			else if (value.equals("right")) getElement().getStyle().setFloat(Float.NONE);
			else if (value.isEmpty()) getElement().getStyle().clearFloat();
		}
		else if (css == Css.OPACITY) {
			if (value.isEmpty()) getElement().getStyle().clearOpacity();
			else getElement().getStyle().setOpacity(Double.parseDouble(value));
		}
		else setCss(css.getName(), value);
	}

	@Override
	public void setCss(String name, String value) {
		DOM.setStyleAttribute(getElement(), name, value);
//		getElement().getStyle().setProperty(name, value);
	}

	@Override
	public IView setNode(Object key) {
//		Element element = null;
//		if (key == null) element = DOM.createElement("element");
//		if (key instanceof Element) element = (Element) key;
//		if (element != null) setElement(element);
//		_node = (Node) key;
//		return this;
		Element element = null;
		if (key == null) element = DOM.createElement("element");
		if (key instanceof String) element = DOM.createElement("element");
		else if (key instanceof Element) element = (Element) key;
		if (element != null) setElement(element);
		_node = (Node) key;
		return this;
		
	}
	
	@Override
	public IView setNode(NodeType type, String nodeValue) {
		if (type == NodeType.ELEMENT) return setNode(nodeValue);
		else if (type == NodeType.TEXT) _node = getDocument().createTextNode(nodeValue);
		//TEST this very well!!!
		else if (type == NodeType.COMMENT) {
			Element ghost = DOM.createDiv();
			ghost.setInnerHTML("<!--"+nodeValue+"-->");
			_node = ghost.getFirstChild();
		}		
		//TODO how to create comment node???
		return this;
	}

	@Override
	public void setProperty(String propName, Object propValue) {
		if (propValue instanceof Boolean) 
			DOM.setElementPropertyBoolean(getElement(), propName, (Boolean) propValue);							
		else if (propValue instanceof Integer) 
			DOM.setElementPropertyInt(getElement(), propName, (Integer) propValue);			
		else 
			DOM.setElementProperty(getElement(), propName, (String) propValue);
//		getElement().setPropertyObject(propName, propValue);
	}

	@Override
	public void show() {
		getElement().getStyle().clearDisplay();
	}

	@Override
	public String text() {
		return getElement().getInnerText();
	}
	
	@Override
	public void text(String text) {
		getElement().setInnerText(text);
	}
		
	@Override
	public void trigger(String event, HashMap<String,List<Handler>> handlers) {
		//the param handlers is useless here but not in DeadView. To not provide two methods,
		//here is just ignored.
		Events.trigger(event, this);
	}
}